﻿using System;
using System.Windows.Threading;
using Red.Controls.Shell;
using Red.Core;

namespace Red.Controls.ViewModels
{
    /// <summary>
    /// View model for app monitor.
    /// </summary>
    public sealed class AppMonitorViewModel : ViewModel
    {
        #region Data

        private bool _monitoringEnabled = true;
        private string _status = "Status: unknown";
        private string _actionsHandled = "Actions handled: unknown";
        private string _actionsPending = "Actions pending: unknown";
        private string _actionExceptionsReported = "Action exceptions: unknown";
        private string _actionsOutOfOrder = "Actions out of order: unknown";
        private string _minRequestTime = "Min request time: unknown";
        private string _maxRequestTime = "Max request time: unknown";

        /// <summary>
        /// App monitor for gathering statistics.
        /// </summary>
        private AppMonitor _appMonitor;

        /// <summary>
        /// Internal timer for updating some of the statistics view models.
        /// </summary>
        private DispatcherTimer _internalTimer;

        #endregion

        #region Properties

        /// <summary>
        /// Subview model for received data statistics.
        /// </summary>
        public AppMonitorTransferViewModel TransferReceived { get; private set; }

        /// <summary>
        /// Subview model for sent data statistics.
        /// </summary>
        public AppMonitorTransferViewModel TransferSent { get; private set; }

        /// <summary>
        /// Subview model for interop debug console
        /// </summary>
        public InteropDebugConsoleViewModel InteropDebugConsole { get; private set; }

        /// <summary>
        /// If enabled, view model is gathering data from app monitor.
        /// </summary>
        public bool MonitoringEnabled
        {
            get { return _monitoringEnabled; }
            set
            {
                _monitoringEnabled = value;
                if ( value )
                    EnableMonitoring();
                else
                    DisableMonitoring();

                OnPropertyChanged();
            }
        }

        /// <summary>
        /// Status of the connection between editor and app.
        /// </summary>
        public string Status
        {
            get { return _status; }
            set
            {
                if ( _status != value )
                {
                    _status = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Number of actions handled so far.
        /// </summary>
        public string ActionsHandled
        {
            get { return _actionsHandled; }
            set
            {
                if ( _actionsHandled != value )
                {
                    _actionsHandled = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Number of actions pending to be handled.
        /// </summary>
        public string ActionsPending
        {
            get { return _actionsPending; }
            set
            {
                if ( _actionsPending != value )
                {
                    _actionsPending = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Number of actions which had exceptions reported as response.
        /// </summary>
        public string ActionExceptionsReported
        {
            get { return _actionExceptionsReported; }
            set
            {
                if ( _actionExceptionsReported != value )
                {
                    _actionExceptionsReported = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Number of actions out of order.
        /// </summary>
        public string ActionsOutOfOrder
        {
            get { return _actionsOutOfOrder; }
            set
            {
                if ( _actionsOutOfOrder != value )
                {
                    _actionsOutOfOrder = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Minimum time between send interop expression and receive interop 
        /// </summary>
        public string MinRequestTime
        {
            get { return _minRequestTime; }
            set
            {
                if ( _minRequestTime != value )
                {
                    _minRequestTime = value;
                    OnPropertyChanged();
                }
            }
        }

        /// <summary>
        /// Maximum time between send interop expression and receive interop 
        /// </summary>
        public string MaxRequestTime
        {
            get { return _maxRequestTime; }
            set
            {
                if ( _maxRequestTime != value )
                {
                    _maxRequestTime = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructs.
        /// </summary>
        /// <param name="appMonitor">For gathering statistics data.</param>
        public AppMonitorViewModel( AppMonitor appMonitor )
        {
            _appMonitor = appMonitor;

            InteropDebugConsole = new InteropDebugConsoleViewModel();
            appMonitor.NewInteropDebugEntry += InteropDebugConsole.OnNewInteropDebugEntry;
            appMonitor.EnableInteropDebugConsole();

            TransferReceived = new AppMonitorTransferViewModel();
            TransferSent = new AppMonitorTransferViewModel();
            _internalTimer = new DispatcherTimer();
            _internalTimer.Interval = new TimeSpan( 0, 0, 1 );      // Called each second
            _internalTimer.Tick += OnInternalTick;

            // First init
            if ( MonitoringEnabled )
                EnableMonitoring();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Enable gathering data from app monitor.
        /// </summary>
        private void EnableMonitoring()
        {
            _appMonitor.OnTrackBytesReceived += AddSampleReceived;
            _appMonitor.OnTrackBytesSent += AddSampleSent;

            _appMonitor.OnFreeze += OnFreeze;
            _appMonitor.OnConnectionLost += OnConnectionLost;
            _appMonitor.OnCrash += OnCrash;
            _appMonitor.OnFine += OnReady;
            _internalTimer.IsEnabled = true;
        }

        /// <summary>
        /// Disable gathering data from app monitor.
        /// </summary>
        private void DisableMonitoring()
        {
            _appMonitor.OnTrackBytesReceived -= AddSampleReceived;
            _appMonitor.OnTrackBytesSent -= AddSampleSent;

            _appMonitor.OnFreeze -= OnFreeze;
            _appMonitor.OnConnectionLost -= OnConnectionLost;
            _appMonitor.OnCrash -= OnCrash;
            _appMonitor.OnFine -= OnReady;
            _internalTimer.IsEnabled = false;
        }

        /// <summary>
        /// Collect sample for received data.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="value"></param>
        private void AddSampleReceived( DateTime time, ulong value )
        {
            RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Background, new Action(
            () =>
            {
                if ( RedApplication.Current != null )
                {
                    TransferReceived.AddSample( time, value );
                }
            } ) );
        }

        /// <summary>
        /// Collect sample for sent data.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="value"></param>
        private void AddSampleSent( DateTime time, ulong value )
        {
            RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Background, new Action(
            () =>
            {
                if ( RedApplication.Current != null )
                {
                    TransferSent.AddSample( time, value );
                }
            } ) );
        }

        /// <summary>
        /// Called on editor-app connection freeze (watchdog is not getting responses, might mean that the network is too busy).
        /// </summary>
        private void OnFreeze()
        {
            Status = "Status: freeze";
        }

        /// <summary>
        /// Called on editor-app connection lost.
        /// </summary>
        private void OnConnectionLost()
        {
            Status = "Status: connection lost";
        }

        /// <summary>
        /// Called on app crash.
        /// </summary>
        /// <param name="callstack"></param>
        private void OnCrash( string callstack )
        {
            Status = "Status: crash";
        }

        /// <summary>
        /// Called when app is ready (frequently).
        /// </summary>
        private void OnReady()
        {
            if ( _appMonitor.ActionsWaitingForResponse > 0 )
                Status = "Status: busy";
            else
                Status = "Status: ready";
        }

        /// <summary>
        /// Internal statistics view models update.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnInternalTick( object sender, EventArgs e )
        {
            ActionsHandled = "Actions handled: " + _appMonitor.ActionsHandled;
            ActionsPending = "Actions pending: " + _appMonitor.ActionsWaitingForResponse;
            ActionExceptionsReported = "Action exceptions: " + _appMonitor.RemoteActionExceptions;
            ActionsOutOfOrder = "Actions out of order: " + _appMonitor.ActionsOutOfOrder;
            MinRequestTime = "Min request time: " + _appMonitor.MinRequestTime + "s";
            MaxRequestTime = "Max request time: " + _appMonitor.MaxRequestTime + "s";
        }

        #endregion

    }
}
